﻿// Coding Guideline
// 1. Use PascalCasing for type and method names and constants
// 2. Use camelCasing for local variable names and method arguments
// 3. Prefix private member variables with m_, such as m_PascalCasing
// 4. Convert tab to 4 spaces automatically
// 5. Public interfaces first in class declaration
// 6. Always place an open curly brace ({) in a new line.
// 7. Number restrict: file = 500 lines; method = 200 lines; arguments = 5; line = 120 chars; members per interface = 20;
// 8. Defensive programming: every fifth lin is an assertion
// 9. Make only the most necessary types public, mark others as internal/private
// 10. Avoid function calls in boolean conditional statements, assign into local variables and check on them
// 11. Use as, not explicitly casting, to cast a type.
// 12. Use String.Empty to represent an empty string, it is not "", and also not null.
// 13. Using application logging and tracing.
// 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

// Visual Studio Automation
using Extensibility;
using EnvDTE;
using EnvDTE80;

namespace VSAddInFramework
{
    // AddIn's base class
    [ComVisibleAttribute(true)]
    public abstract class VSAddInMgr : IDTExtensibility2, IDTCommandTarget
    {
        // IDTExtensibility2 methods
        public void OnAddInsUpdate(ref Array custom)
        {
            _OnAddInsUpdate(ref custom);
        }
        public void OnBeginShutdown(ref Array custom)
        {
            _OnBeginShutdown(ref custom);
        }
        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            m_ApplicationObject = application as DTE2;
            Debug.Assert(m_ApplicationObject != null);
            m_AddInInstance = addInInst as EnvDTE.AddIn;
            Debug.Assert(m_AddInInstance != null);

            CreateCommands();

            // Create external commands that saved in an xml file.
            if (ExternalCmd.Instance != null)
            {
                ExternalCmd.Instance.CreateCommands();
            }

            // If addin is loaded on startup, we will create commands UI in OnStartupComplete

            // If addin is loaded manually after VS starts, let's create commands UI here.
            if (ext_ConnectMode.ext_cm_AfterStartup == connectMode)
            {
                VSCommandMgr.CreateCommandsUI();
            }

            _OnConnection(Application, connectMode, addInInst, ref custom);
        }
        public void OnDisconnection(ext_DisconnectMode removeMode, ref Array custom)
        {
            VSCommandMgr.DestroyCommandsUI();
            _OnDisconnection(removeMode, ref custom);
        }
        public void OnStartupComplete(ref Array custom)
        {
            VSCommandMgr.CreateCommandsUI();
            _OnStartupComplete(ref custom);
        }

        // IDTCommandTarget methods
        public void Exec(string cmdName, vsCommandExecOption executeOption, ref object variantIn, ref object variantOut, ref bool handled)
        {
            handled = false;
            if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
            {
                VSCommand cmd = VSCommandMgr.GetCommand(cmdName);
                Debug.Assert(cmd != null);
                if(cmd != null)
                {
                    cmd.Execute();
                }
                handled = true;
            }

            _Exec(cmdName, executeOption, ref variantIn, ref variantOut, ref handled);
        }
        public virtual void QueryStatus(string cmdName, vsCommandStatusTextWanted neededText, ref vsCommandStatus statusOption, ref object commandText)
        {
            if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
            {
                VSCommand cmd = VSCommandMgr.GetCommand(cmdName);
                Debug.Assert(cmd != null);
                if(cmd != null)
                {
                    cmd.QueryStatus(ref statusOption);
                }
            }
            _QueryStatus(cmdName, neededText, ref statusOption, ref commandText);
        }

        // Concrete AddIn needs to override following methods
        public abstract void CreateCommands();

        // Give user a chance to do additonal work for a specific events.
        // generally, you never need to as the framework has done everything for you.
        public virtual void _OnAddInsUpdate(ref Array custom) { }
        public virtual void _OnBeginShutdown(ref Array custom) { }
        public virtual void _OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom) { }
        public virtual void _OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom) { }
        public virtual void _OnStartupComplete(ref Array custom) { }
        public virtual void _Exec(string CmdName, vsCommandExecOption ExecuteOption, ref object VariantIn, ref object VariantOut, ref bool Handled) { }
        public virtual void _QueryStatus(string CmdName, vsCommandStatusTextWanted NeededText, ref vsCommandStatus StatusOption, ref object CommandText) { }

        // Singleton
        protected VSAddInMgr() 
        {
            Instance = this;
        }
        protected static VSAddInMgr m_AddInMgrInstance;
        public static VSAddInMgr Instance 
        {
            get 
            { 
                Debug.Assert(m_AddInMgrInstance != null); 
                return m_AddInMgrInstance; 
            } 
            set 
            {
                m_AddInMgrInstance = value;
            }
        }

        // Data members
        public string ProgID
        {
            get { return AddIn.ProgID; }
        }
        public DTE2 Application
        {
            get { return m_ApplicationObject; }
        }
        public EnvDTE.AddIn AddIn
        {
            get { return m_AddInInstance; }
        }

        private DTE2 m_ApplicationObject;
        private EnvDTE.AddIn m_AddInInstance;
    }
}
